use crate::config::SEARCH_RESULT_FILE_NAME;
use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::{
    fmt,
    fs::File,
    io::{BufRead, BufReader, Write},
    path::Path,
};

/// 搜索条目
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Entry {
    /// 文件路径
    pub file_path: String,
    /// 匹配的内容
    pub matched_content: String,
    /// 创建时间
    #[serde(with = "chrono::serde::ts_seconds")]
    pub created_at: DateTime<Utc>,
}

impl fmt::Display for Entry {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "Entry {{ file_path: {}, matched_content: {}, created_at: {} }}",
            self.file_path, self.matched_content, self.created_at
        )
    }
}

/// 将Entry转换为字符串表示形式
///
/// # Arguments
/// * `entry` - 要转换的Entry引用
///
/// # Returns
/// 返回格式化后的字符串
pub fn entry_to_string(entry: &Entry) -> String {
    let mut result = String::new();
    result.push_str("<entry>\n");
    result.push_str(&format!("  <file_path>{}</file_path>\n", entry.file_path));
    result.push_str(&format!(
        "  <matched_content>{}</matched_content>\n",
        entry.matched_content
    ));
    result.push_str(&format!(
        "  <created_at>{}</created_at>\n",
        entry.created_at
    ));
    result.push_str("</entry>\n");
    result
}

/// 解析entry的纯函数
///
/// # Arguments
/// * `lines` - 包含entry内容的字符串切片
///
/// # Returns
/// 解析成功的Entry或None
pub fn parse_entry(lines: &[&str]) -> Option<Entry> {
    let mut entry = Entry {
        file_path: String::new(),
        matched_content: String::new(),
        created_at: Utc::now(),
    };

    let mut has_file_path = false;
    let mut has_content = false;

    for line in lines {
        let line = line.trim();
        if line.is_empty() {
            continue;
        }

        // 查找开始标签和结束标签的位置
        let tag_start = line.find('<')?;
        let tag_end = line.find('>')?;

        if tag_start < tag_end {
            let tag = &line[tag_start + 1..tag_end];

            // 检查是否是闭合标签（以/开头）
            if tag.starts_with('/') {
                continue;
            }

            // 查找闭合标签的位置
            let closing_tag = format!("</{}>", tag);
            let content_end = line.find(&closing_tag)?;

            // 提取标签内容（在开始标签后和闭合标签前的内容）
            let content = &line[tag_end + 1..content_end].trim();

            log::debug!("Parsing tag: {}, content: '{}'", tag, content);

            match tag {
                "file_path" => {
                    entry.file_path = content.to_string();
                    has_file_path = true;
                }
                "matched_content" => {
                    entry.matched_content = content.to_string();
                    has_content = true;
                }
                "created_at" => {
                    if let Ok(dt) = content.parse::<DateTime<Utc>>() {
                        entry.created_at = dt;
                    } else {
                        log::error!("Failed to parse datetime: '{}'", content);
                        return None;
                    }
                }
                _ => {}
            }
        }
    }

    // 检查必填字段是否为空
    if has_file_path && has_content {
        Some(entry)
    } else {
        None
    }
}

/// 从.search文件创建条目迭代器
pub fn entries_iter(search_file_path: &std::path::Path) -> impl Iterator<Item = Entry> {
    // 尝试打开文件，如果失败则返回空迭代器
    let file = match File::open(search_file_path) {
        Ok(f) => f,
        Err(e) => {
            log::error!(
                "Failed to open search file at {}: {}",
                search_file_path.display(),
                e
            );
            return EntryIterator { reader: None };
        }
    };

    EntryIterator {
        reader: Some(BufReader::new(file)),
    }
}

struct EntryIterator {
    reader: Option<BufReader<File>>,
}

impl Iterator for EntryIterator {
    type Item = Entry;

    fn next(&mut self) -> Option<Self::Item> {
        let reader = match &mut self.reader {
            Some(r) => r,
            None => return None,
        };

        let mut entry_lines = Vec::new();
        let mut in_entry = false;

        loop {
            let mut buf = String::new();
            match reader.read_line(&mut buf) {
                Ok(0) => return None, // EOF
                Ok(_) => {
                    let line = buf.trim();

                    if line == "<entry>" {
                        if in_entry {
                            log::error!("Found nested <entry> tag without closing previous entry");
                            return None;
                        }
                        in_entry = true;
                        entry_lines.clear(); // 开始新的entry，清空之前的行
                        continue;
                    } else if line == "</entry>" {
                        if in_entry {
                            in_entry = false;
                            let entry_lines_ref: Vec<&str> =
                                entry_lines.iter().map(AsRef::as_ref).collect();
                            if let Some(entry) = parse_entry(&entry_lines_ref) {
                                return Some(entry);
                            } else {
                                log::error!("Failed to parse entry from lines: {:?}", entry_lines);
                                // 即使解析失败，也继续处理下一个entry
                                entry_lines.clear();
                                continue;
                            }
                        }
                        continue;
                    }

                    if in_entry {
                        entry_lines.push(line.to_string());
                    }
                }
                Err(e) => {
                    log::error!("Error reading from search file: {}", e);
                    return None;
                }
            }
        }
    }
}

/// 将条目保存到.search文件
///
/// # Arguments
/// * `entries` - 要保存的条目切片
/// * `dir` - 保存目录
///
/// # Returns
/// 返回`Result<()>`，成功时返回`Ok(())`，失败时返回错误
pub fn save_entries(entries: &[Entry], working_dir: &Path) -> Result<()> {
    let search_file = working_dir.join(SEARCH_RESULT_FILE_NAME);

    // 创建或覆盖.search文件
    let mut file = File::create(&search_file)
        .with_context(|| format!("Failed to create search file at {}", search_file.display()))?;

    // 以<entry>块的形式写入每个条目
    for entry in entries {
        file.write_all(entry_to_string(entry).as_bytes())?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::TimeZone;
    use std::fs;
    use tempfile::tempdir;

    #[test]
    fn test_parse_entry_valid() {
        let lines = vec![
            "<file_path>test.txt</file_path>",
            "<matched_content>Hello World</matched_content>",
            "<created_at>2023-01-01T00:00:00Z</created_at>",
        ];

        let entry = parse_entry(&lines).unwrap();

        assert_eq!(entry.file_path, "test.txt");
        assert_eq!(entry.matched_content, "Hello World");
        assert_eq!(
            entry.created_at,
            Utc.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap()
        );
    }
    #[test]
    fn test_parse_entry_valid_multiple_lines() {
        let lines = vec![
            "<file_path>test.txt</file_path>",
            r#"<matched_content>Hello World
this is a valid conent.

end;
        </matched_content>"#,
            "<created_at>2023-01-01T00:00:00Z</created_at>",
        ];

        let entry = parse_entry(&lines).unwrap();

        assert_eq!(entry.file_path, "test.txt");
        assert_eq!(
            entry.matched_content,
            r#"Hello World
this is a valid conent.

end;"#
        );
        assert_eq!(
            entry.created_at,
            Utc.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap()
        );
    }

    #[test]
    fn test_parse_entry_missing_fields() {
        let lines = vec![
            "<file_path>test.txt</file_path>",
            "<created_at>2023-01-01T00:00:00Z</created_at>",
        ];

        assert!(parse_entry(&lines).is_none());
    }

    #[test]
    fn test_parse_entry_invalid_date() {
        let lines = vec![
            "<file_path>test.txt</file_path>",
            "<matched_content>Hello World</matched_content>",
            "<created_at>invalid-date</created_at>",
        ];

        let entry = parse_entry(&lines);

        assert!(entry.is_none());
    }

    #[test]
    fn test_parse_entry_empty() {
        assert!(parse_entry(&[]).is_none());
    }

    #[test]
    fn test_entry_to_string() {
        let entry = Entry {
            file_path: "test.txt".to_string(),
            matched_content: "Hello World".to_string(),
            created_at: Utc.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap(),
        };

        let expected = "<entry>\n  <file_path>test.txt</file_path>\n  <matched_content>Hello World</matched_content>\n  <created_at>2023-01-01 00:00:00 UTC</created_at>\n</entry>\n";
        assert_eq!(entry_to_string(&entry), expected);
    }

    #[test]
    fn test_entries_iter_multiple_entries() {
        let temp_dir = tempdir().unwrap();
        let search_file = temp_dir.path().join(".search");

        let content = r#"<entry>
  <file_path>file1.txt</file_path>
  <matched_content>content1</matched_content>
  <created_at>2023-01-01T00:00:00Z</created_at>
</entry>
<entry>
  <file_path>file2.txt</file_path>
  <matched_content>content2</matched_content>
  <created_at>2023-01-02T00:00:00Z</created_at>
</entry>
<entry>
  <file_path>file3.txt</file_path>
  <matched_content>content3</matched_content>
  <created_at>2023-01-03T00:00:00Z</created_at>
</entry>"#;

        fs::write(&search_file, content).unwrap();

        let mut iter = entries_iter(&search_file);

        let entry1 = iter.next().unwrap();
        assert_eq!(entry1.file_path, "file1.txt");
        assert_eq!(entry1.matched_content, "content1");

        let entry2 = iter.next().unwrap();
        assert_eq!(entry2.file_path, "file2.txt");
        assert_eq!(entry2.matched_content, "content2");

        let entry3 = iter.next().unwrap();
        assert_eq!(entry3.file_path, "file3.txt");
        assert_eq!(entry3.matched_content, "content3");

        assert!(iter.next().is_none());
    }

    #[test]
    fn test_entries_iter_empty_file() {
        let temp_dir = tempdir().unwrap();
        let search_file = temp_dir.path().join(".search");
        fs::write(&search_file, "").unwrap();

        let mut iter = entries_iter(&search_file);
        assert!(iter.next().is_none());
    }

    #[test]
    fn test_entries_iter_nonexistent_file() {
        let temp_dir = tempdir().unwrap();
        let search_file = temp_dir.path().join("nonexistent.search");

        let mut iter = entries_iter(&search_file);
        assert!(iter.next().is_none());
    }
}
